package com.travel.controller;

import com.travel.common.Result;
import com.travel.domain.VO.ProductVO;
import com.travel.domain.entity.ProductInfo;
import com.travel.service.ProductService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;
import java.util.concurrent.TimeUnit;

@Slf4j
@RestController
@RequestMapping("/product")
public class ProductController {

    @Autowired
    private ProductService productService;

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    // 商品列表缓存键
    private static final String ALL_PRODUCTS_CACHE_KEY = "product:all";

    // 商品详情缓存键
    private static final String PRODUCT_INFO_CACHE_KEY = "product:info:";

    /**
     * 获取所有产品
     * @return 产品列表
     */
    @GetMapping("/getAllProducts")
    public Result<List<ProductVO>> getAllProducts() {
        // 尝试从缓存中获取所有产品列表
        List<ProductVO> cachedProductList = (List<ProductVO>) redisTemplate.opsForValue().get(ALL_PRODUCTS_CACHE_KEY);

        if (cachedProductList != null) {
            // 如果缓存中有数据，直接返回缓存数据
            return Result.success(cachedProductList);
        }

        try {
            // 如果缓存中没有数据，则从数据库查询
            List<ProductVO> productVOList = productService.selectAllProducts();

            // 将查询结果存入缓存，并设置过期时间（如 10 分钟）
            redisTemplate.opsForValue().set(ALL_PRODUCTS_CACHE_KEY, productVOList);

            return Result.success(productVOList);
        } catch (Exception e) {
            log.error("获取产品列表失败: {}", e.getMessage());
            return Result.error("获取产品列表失败");
        }
    }


    /**
     * 获取单个产品信息
     * @param productId 产品ID
     * @return 产品信息
     */
    // 解决缓存击穿问题
    @GetMapping("/getProductInfo/{productId}")
    public Result<ProductInfo> getProductInfo(@PathVariable Long productId) {
        // 缓存的 Key
        String cacheKey = PRODUCT_INFO_CACHE_KEY + productId;
        // 分布式锁的 Key
        String lockKey = "lock:product:" + productId;

        // 1. 尝试从缓存中获取单个产品信息
        ProductInfo cachedProductInfo = (ProductInfo) redisTemplate.opsForValue().get(cacheKey);
        if (cachedProductInfo != null) {
            // 如果缓存中有数据，直接返回缓存数据
            return Result.success(cachedProductInfo);
        }

        // 2. 缓存失效，尝试加锁
        try {
            // 使用 Redis 分布式锁 (SETNX)
            boolean lock = redisTemplate.opsForValue().setIfAbsent(lockKey, "1", 10, TimeUnit.SECONDS);
            if (lock) {
                // 加锁成功，当前线程负责查询数据库并更新缓存
                ProductInfo productInfo = productService.getProductInfo(productId);

                if (productInfo != null) {
                    // 将查询结果存入缓存，并设置过期时间（如 5 分钟）
                    redisTemplate.opsForValue().set(cacheKey, productInfo, 5, TimeUnit.MINUTES);
                }

                // 释放锁
                redisTemplate.delete(lockKey);
                return Result.success(productInfo);
            } else {
                // 3. 加锁失败，其他线程等待
                Thread.sleep(50); // 等待一段时间后重试
                return getProductInfo(productId); // 递归调用，重新获取数据
            }
        } catch (Exception e) {
            log.error("获取产品信息失败: {}", e.getMessage());
            return Result.error("获取产品信息失败");
        }
    }
}
